#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi(Noeud* expBool, Noeud* seqInst, Noeud* sinon) {
    
    this->expbool = expBool;
    this->seqInst = seqInst;
    this->sinon = sinon;
}

void NoeudInstSi::ajouteSeqInst(Noeud* seqInst) {
    this->seqInst = seqInst;
}

void NoeudInstSi::ajouteSinon(Noeud* sinon) {
    this->sinon = sinon;
}

int NoeudInstSi::getValeur() {
    
    if (expbool->getValeur())
        seqInst->getValeur();
    else if (sinon!=NULL) sinon->getValeur();
    return 0;
}

void NoeudInstSi::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction si" << endl;
    expbool->afficher(indentation+1);
    seqInst->afficher(indentation+1);
    if(sinon != NULL)
        sinon->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(Noeud* expressBool, Noeud* seqInst) {
  this->expressBool=expressBool;
  this->tabSeqInst=seqInst;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstTq::getValeur() {
 // on évalue l'expression
  while(expressBool->getValeur()){
     tabSeqInst->getValeur();
  }
  return 42; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstTq::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - tantque" << endl;
  expressBool->afficher(indentation+1);   // on affiche variable et expression
  tabSeqInst->afficher(indentation+1); // en augmentant l'indentation
}


////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter(Noeud* expressBool, Noeud* seqInst) {
  this->expressBool=expressBool;
  this->tabSeqInst=seqInst;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstRepeter::getValeur() {
 // on évalue l'expression
  do{
     tabSeqInst->getValeur();
  } while(expressBool->getValeur());
  return 42; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstRepeter::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - repeter" << endl;
  expressBool->afficher(indentation+1);   // on affiche variable et expression
  tabSeqInst->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
//NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* initialisation, Noeud* condition,
        Noeud* operation, Noeud* seqInst) {
    this->initialisation = initialisation;
    this->condition = condition;
    this->operation = operation;
    this->seqInst = seqInst;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstPour::getValeur() {
    
    initialisation->getValeur();
    while(condition->getValeur()) {
        seqInst->getValeur();
        operation->getValeur();
    }
    return 1;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - pour" << endl;
    initialisation->afficher(indentation+1);
    condition->afficher(indentation+1);
    operation->afficher(indentation+1);
    seqInst->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::setInitialisation(Noeud* initialisation) {
    this->initialisation = initialisation;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::setCondition(Noeud* condition) {
    this->condition = condition;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::setOperation(Noeud* operation) {
    this->operation = operation;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::setSeqInst(Noeud* seqInst) {
    this->seqInst = seqInst;
}


////////////////////////////////////////////////////////////////////////////////
//NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire::NoeudInstEcrire() {
    chaine = "";
    expression = NULL;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstEcrire::getValeur() {
    if(chaine == "") {
        cout << expression->getValeur() << endl;
        return expression->getValeur();
    }
    else {
        cout << chaine << endl;
        return 1;
    }
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - ecrire" << endl;
    if(chaine != "") {
        cout << setw(4*indentation) << " ";
        cout << chaine << endl;
    }
    else {
        expression->afficher(indentation+1);
    }
}
////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::ajouteChaine(string chaine) {
    chaine.erase(0,1);
    chaine.erase(chaine.length()-1,1);
    this->chaine = chaine;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::ajouteExpression(Noeud* expression) {
    this->expression = expression;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////

NoeudInstLire::NoeudInstLire(Noeud* variable) {
    this->variable = variable;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstLire::getValeur() {
    int valeur;
    cin >> valeur;
    ((SymboleValue*) variable)->setValeur(valeur);
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstLire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Lire" << endl;
    variable->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstLire::ajouteVariable(Noeud* variable) {
    this->variable = variable;
}
////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() { 
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  if      (this->operateur=="+") valeur = og+od; 
  else if (this->operateur=="-") valeur = og-od;
  else if (this->operateur=="*") valeur = og*od;
  else if (this->operateur=="==") valeur = (int)(og == od);
  else if (this->operateur=="!=") valeur = (int)(og != od);
  else if (this->operateur==">") valeur = (int)(og > od);
  else if (this->operateur==">=") valeur = (int)(og>=od);
  else if (this->operateur=="<") valeur = (int)(og < od);
  else if (this->operateur=="<=") valeur = (int)(og <= od);
  else if (this->operateur=="ou") valeur = (int)(og || od);
  else if (this->operateur=="et") valeur = (int)(og && od);
  else  // this->operateur=="/"
	if (od!=0)
		valeur = og/od;
  	else {
	  	cout << "Erreur pendant l'interpretation : division par zero" << endl;
	  	exit(0); // plus tard on levera une exception
  	}
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}
